package com.luo.gmall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.luo.gmall.common.bean.ResponseVo;
import com.luo.gmall.pms.entity.*;
import com.luo.gmall.search.feign.GmallPmsClient;
import com.luo.gmall.search.feign.GmallWmsClient;
import com.luo.gmall.search.repository.GoodsRepository;
import com.luo.gmall.search.vo.*;
import com.luo.gmall.wms.entity.WareSkuEntity;
import com.rabbitmq.client.Channel;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.springframework.amqp.core.Message;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import com.luo.gmall.search.service.SearchService;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author Zoro
 * @date 2021年11月09
 */
@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private RestHighLevelClient highLevelClient;

    @Autowired
    private GmallPmsClient gmallPmsClient;

    @Autowired
    private GmallWmsClient gmallWmsClient;

    @Autowired
    private GoodsRepository goodsRepository;

    @Override
    public void createIndex(Long spuId, Channel channel, Message message) throws IOException {

        // 根据spuId查询spu
        ResponseVo<SpuEntity> spuEntityResponseVo = gmallPmsClient.querySpuById(spuId);
        SpuEntity spuEntity = spuEntityResponseVo.getData();
        if (spuEntity == null) {
            // 确认消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }
        ResponseVo<List<SkuEntity>> skuResponseVo = gmallPmsClient.querySkuBySpuId(spuId);
        List<SkuEntity> skuEntities = skuResponseVo.getData();
        if (!com.alibaba.nacos.client.naming.utils.CollectionUtils.isEmpty(skuEntities)) {
            // 根据品牌的id查询品牌。同一个spu品牌是一样的，并且只有sku集合不为空时，才需要品牌
            BrandEntity brandEntity = gmallPmsClient.queryBrandById(spuEntity.getBrandId()).getData();
            // 根据分类id查询分类。
            CategoryEntity categoryEntity = gmallPmsClient.queryCategoryById(spuEntity.getCategoryId()).getData();
            // 根据分类id和spuId查询基本类型的规格参数和值
            List<SpuAttrValueEntity> spuAttrValueEntities = gmallPmsClient.querySpuAttrValueListBySpuIdAndCategoryId(spuEntity.getCategoryId(), spuEntity.getId()).getData();
            // 将sku转换成goods
            List<Goods> goodsList = skuEntities.stream().map(skuEntity -> {
                Goods goods = new Goods();
                goods.setSkuId(skuEntity.getId());
                goods.setTitle(skuEntity.getTitle());
                goods.setSubTitle(skuEntity.getSubtitle());
                goods.setDefaultImage(skuEntity.getDefaultImage());
                goods.setPrice(skuEntity.getPrice().doubleValue());

                // 设置根据时间排序属性
                goods.setCreateTime(spuEntity.getCreateTime());
                ResponseVo<List<WareSkuEntity>> wareSkuResponseVo = gmallWmsClient.queryWareSkuBySkuId(skuEntity.getId());
                List<WareSkuEntity> wareSkuEntities = wareSkuResponseVo.getData();
                if (!CollectionUtils.isEmpty(wareSkuEntities)) {
                    // 设置销量
                    goods.setSales(wareSkuEntities.stream().map(WareSkuEntity::getSales).reduce((a, b) -> a + b).get());
                    // 设置是否有存货
                    goods.setStore(wareSkuEntities.stream().anyMatch(wareSkuEntity -> wareSkuEntity.getStock() - wareSkuEntity.getStockLocked() > 0));
                }

                if (brandEntity != null) {
                    // 设置品牌id
                    goods.setBrandId(brandEntity.getId());
                    // 设置品牌名称
                    goods.setBrandName(brandEntity.getName());
                    // 设置logo
                    goods.setLogo(brandEntity.getLogo());
                }
                if (categoryEntity != null) {
                    // 设置分类id
                    goods.setCategoryId(categoryEntity.getId());
                    // 设置分类名称
                    goods.setCategoryName(categoryEntity.getName());
                }
                // 定义规格参数
                List<SearchAttrValueVo> searchAttrValueVoList = new ArrayList<>();
                // 设置spu检索类型的规格参数
                if (!CollectionUtils.isEmpty(spuAttrValueEntities)) {
                    searchAttrValueVoList.addAll(spuAttrValueEntities.stream().map(spuAttrValueEntity -> {
                        SearchAttrValueVo searchAttrValueVo = new SearchAttrValueVo();
                        BeanUtils.copyProperties(spuAttrValueEntity, searchAttrValueVo);
                        return searchAttrValueVo;
                    }).collect(Collectors.toList()));
                }

                // 根据分类id和spuId查询基本类型的规格参数和值
                List<SkuAttrValueEntity> skuAttrValueEntities = gmallPmsClient.querySkuAttrValueListBySkuIdAndCategoryId(skuEntity.getCategoryId(), skuEntity.getId()).getData();

                // 设置sku检索类型的规格参数(销售类型)
                if (!CollectionUtils.isEmpty(skuAttrValueEntities)) {
                    searchAttrValueVoList.addAll(skuAttrValueEntities.stream().map(skuAttrValueEntity -> {
                        SearchAttrValueVo searchAttrValueVo = new SearchAttrValueVo();
                        BeanUtils.copyProperties(skuAttrValueEntity, searchAttrValueVo);
                        return searchAttrValueVo;
                    }).collect(Collectors.toList()));
                }
                // 设置规格参数
                goods.setSearchAttrs(searchAttrValueVoList);
                return goods;
            }).collect(Collectors.toList());
            // 批量保存到索引库中
            goodsRepository.saveAll(goodsList);
        }
        // 确认消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);

    }

    @Override
    public SearchResponseVo search(SearchParamVo searchParamVo) {
        try {
            SearchResponse searchResponse = highLevelClient.search(new SearchRequest(new String[]{"goods"}, sourceBuilder(searchParamVo)), RequestOptions.DEFAULT);
            SearchResponseVo searchResponseVo = transform(searchResponse);
            // 设置
            searchResponseVo.setPageNum(searchParamVo.getPageNum());
            searchResponseVo.setPageSize(searchParamVo.getPageSize());
            return searchResponseVo;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private SearchResponseVo transform(SearchResponse searchResponse) {
        SearchResponseVo searchResponseVo = new SearchResponseVo();
        // 1.获取hits中的数据
        SearchHits hits = searchResponse.getHits();
        // 设置命中数
        searchResponseVo.setTotal(hits.getTotalHits());
        // 设置商品列表数据
        SearchHit[] hitsHits = hits.getHits();
        if (!ArrayUtils.isEmpty(hitsHits)) {
            List<Goods> goodsList = Arrays.stream(hitsHits).map(searchHit -> {
                // 获取每一个_source的数据，将他转换成goods
                String sourceAsString = searchHit.getSourceAsString();
                Goods goods = JSON.parseObject(sourceAsString, Goods.class);
                // 将高亮的标题替换到goods中
                Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
                // 判空
                if (!CollectionUtils.isEmpty(highlightFields)) {
                    HighlightField title = highlightFields.get("title");
                    // 判空
                    if (title != null) {
                        // 判空
                        Text[] fragments = title.getFragments();
                        if (!ArrayUtils.isEmpty(fragments)) {
                            goods.setTitle(fragments[0].string());
                        }
                    }
                }
                return goods;
            }).collect(Collectors.toList());
            searchResponseVo.setGoodsList(goodsList);
        }
        // 2.获取aggregations中的数据
        Aggregations aggregations = searchResponse.getAggregations();
        if (aggregations != null) {

            // 2.1.分类列表
            ParsedLongTerms categoryIdAgg = aggregations.get("categoryIdAgg");
            List<? extends Terms.Bucket> categoryBuckets = categoryIdAgg.getBuckets();
            // 判断buckets是否为空，则设值
            if (!CollectionUtils.isEmpty(categoryBuckets)) {
                List<CategoryEntity> categoryEntities = categoryBuckets.stream().map(bucket -> {
                    CategoryEntity categoryEntity = new CategoryEntity();
                    // 设置category Id
                    categoryEntity.setId(bucket.getKeyAsNumber().longValue());
                    ParsedStringTerms categoryNameAgg = bucket.getAggregations().get("categoryNameAgg");
                    List<? extends Terms.Bucket> nameAggBuckets = categoryNameAgg.getBuckets();
                    // 判断buckets是否为空，则设值
                    if (!CollectionUtils.isEmpty(nameAggBuckets)) {
                        // 设置category name
                        categoryEntity.setName(nameAggBuckets.get(0).getKeyAsString());
                    }
                    return categoryEntity;
                }).collect(Collectors.toList());
                searchResponseVo.setCategories(categoryEntities);
            }

            // 2.2.品牌分类列表
            ParsedLongTerms brandIdAgg = aggregations.get("brandIdAgg");
            List<? extends Terms.Bucket> brandBuckets = brandIdAgg.getBuckets();
            // 判断buckets是否为空，则设值
            if (!CollectionUtils.isEmpty(brandBuckets)) {
                List<BrandEntity> brandEntities = brandBuckets.stream().map(bucket -> {
                    BrandEntity brandEntity = new BrandEntity();
                    // 设置brand ID
                    brandEntity.setId(bucket.getKeyAsNumber().longValue());
                    ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brandNameAgg");
                    List<? extends Terms.Bucket> brandNameAggBuckets = brandNameAgg.getBuckets();
                    // 判断buckets是否为空，则设值
                    if (!CollectionUtils.isEmpty(brandNameAggBuckets)) {
                        // 设置 brand name
                        brandEntity.setName(brandNameAggBuckets.get(0).getKeyAsString());
                    }
                    ParsedStringTerms brandLogoAgg = bucket.getAggregations().get("brandLogoAgg");
                    List<? extends Terms.Bucket> brandLogoAggBuckets = brandLogoAgg.getBuckets();
                    // 判断buckets是否为空，则设值
                    if (!CollectionUtils.isEmpty(brandLogoAggBuckets)) {
                        // 设置brand logo
                        brandEntity.setLogo(brandLogoAggBuckets.get(0).getKeyAsString());
                    }
                    return brandEntity;
                }).collect(Collectors.toList());

                searchResponseVo.setBrands(brandEntities);
            }
            //2.3 规格分类列表
            ParsedNested attrAgg = aggregations.get("attrAgg");
            ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");
            List<? extends Terms.Bucket> attrIdBuckets = attrIdAgg.getBuckets();
            // 判断buckets是否为空，则设值
            if (!CollectionUtils.isEmpty(attrIdBuckets)) {
                List<SearchResponseAttrValueVo> searchResponseAttrValueVos = attrIdBuckets.stream().map(bucket -> {
                    SearchResponseAttrValueVo searchResponseAttrValueVo = new SearchResponseAttrValueVo();
                    // 设置SearchResponseAttrValueVo attrId
                    searchResponseAttrValueVo.setAttrId(bucket.getKeyAsNumber().longValue());
                    ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
                    List<? extends Terms.Bucket> attrNameAggBuckets = attrNameAgg.getBuckets();
                    // 判断buckets是否为空，则设值
                    if (!CollectionUtils.isEmpty(attrNameAggBuckets)) {
                        // 设置SearchResponseAttrValueVo attrName
                        searchResponseAttrValueVo.setAttrName(attrNameAggBuckets.get(0).getKeyAsString());
                    }
                    ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attrValueAgg");
                    List<? extends Terms.Bucket> attrValueAggBuckets = attrValueAgg.getBuckets();
                    // 判断buckets是否为空，则设值
                    if (!CollectionUtils.isEmpty(attrValueAggBuckets)) {
                        // 设置SearchResponseAttrValueVo attrValue
                        searchResponseAttrValueVo.setAttrValues(attrValueAggBuckets.stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList()));
                    }
                    return searchResponseAttrValueVo;
                }).collect(Collectors.toList());

                searchResponseVo.setFilters(searchResponseAttrValueVos);
            }
        }
        return searchResponseVo;
    }


    /**
     * 构建搜索条件
     */
    private SearchSourceBuilder sourceBuilder(SearchParamVo searchParamVo) {

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        String keyword = searchParamVo.getKeyword();
        // 判断keyword是否为空，否则不查询
        if (StringUtils.isEmpty(keyword)) {
            throw new RuntimeException("没有与“”匹配的商品");
        }
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        sourceBuilder.query(queryBuilder);
        // 1.构建搜索和过滤条件
        // 1.1.构建过滤条件
        queryBuilder.must(QueryBuilders.matchQuery("title", keyword).operator(Operator.AND));

        // 1.2.构建过滤条件
        // 1.2.1.构建品牌过滤
        List<Long> brandId = searchParamVo.getBrandId();
        if (!CollectionUtils.isEmpty(brandId)) {
            queryBuilder.filter(QueryBuilders.termsQuery("brandId", brandId));
        }
        // 1.2.2.构建分组过滤
        List<Long> categoryId = searchParamVo.getCategoryId();
        if (!CollectionUtils.isEmpty(categoryId)) {
            queryBuilder.filter(QueryBuilders.termsQuery("categoryId", categoryId));
        }
        // 1.2.3.构建价格区间过滤
        Double priceFrom = searchParamVo.getPriceFrom();
        Double priceTo = searchParamVo.getPriceTo();
        // 如果priceFrom或priceTo
        if (priceFrom != null || priceTo != null) {
            // 构建一个区间查询
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price");
            queryBuilder.filter(rangeQueryBuilder);
            // 如果priceFrom不为空设为前置区间过滤
            if (priceFrom != null) {
                rangeQueryBuilder.gte(priceFrom);
            }
            // 如果priceTo不为空设为后置区间过滤
            if (priceTo != null) {
                rangeQueryBuilder.lte(priceTo);
            }
        }
        // 1.2.4.构建库存过滤
        Boolean store = searchParamVo.getStore();
        if (store != null) {
            queryBuilder.filter(QueryBuilders.termsQuery("store", store));
        }
        // 1.2.5.构建规格参数过滤 1:12G-18G-19G
        List<String> props = searchParamVo.getProps();
        // 判断构建参数列表是否为空
        if (!CollectionUtils.isEmpty(props)) {
            // 遍历添加嵌套过滤条件
            props.forEach(prop -> {
                // 以:号分割数据:号前为attrId，:号后为attrValue
                String[] attrs = prop.split(":");
                // 判断查询条件合法才进行添加过滤条件
                if (attrs.length == 2 && NumberUtils.isCreatable(attrs[0])) {
                    // 添加attrId过滤数据
                    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
                    boolQueryBuilder.must(QueryBuilders.termQuery("searchAttrs.attrId", attrs[0]));
                    // 添加attrValue过滤数据
                    String[] attrValues = StringUtils.split(attrs[1], "-");
                    boolQueryBuilder.must(QueryBuilders.termsQuery("searchAttrs.attrValue", attrValues));

                    queryBuilder.filter(new NestedQueryBuilder("searchAttrs", boolQueryBuilder, ScoreMode.None));
                }
            });

        }
        // 2.构建排序条件 排序条件：0-综合排序 1-价格降序 2-价格升序 3-销量降序 4-新品降序
        Integer sort = searchParamVo.getSort();
        if (sort != null) {
            switch (sort) {
                case 1:
                    sourceBuilder.sort("price", SortOrder.DESC);
                    break;
                case 2:
                    sourceBuilder.sort("price", SortOrder.ASC);
                    break;
                case 3:
                    sourceBuilder.sort("sales", SortOrder.DESC);
                    break;
                case 4:
                    sourceBuilder.sort("createTime", SortOrder.DESC);
                    break;
                default:
                    sourceBuilder.sort("_score", SortOrder.DESC);
            }
        }
        // 3.构建分页，写死不用判空
        Integer pageNum = searchParamVo.getPageNum();
        Integer pageSize = searchParamVo.getPageSize();
        sourceBuilder.from((pageNum - 1) * pageSize);
        sourceBuilder.size(pageSize);

        // 4.构建高亮
        sourceBuilder.highlighter(new HighlightBuilder().field("title").preTags("<font style='color:red;'>").postTags("</font>"));

        // 5.构建聚合
        // 5.1.分类集合
        sourceBuilder.aggregation(AggregationBuilders.terms("categoryIdAgg").field("categoryId").
                subAggregation(AggregationBuilders.terms("categoryNameAgg").field("categoryName")));

        // 5.2.品牌聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("brandIdAgg").field("brandId").
                subAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName")).
                subAggregation(AggregationBuilders.terms("brandLogoAgg").field("logo")));

        // 5.3.规格参数聚合
        sourceBuilder.aggregation(AggregationBuilders.nested("attrAgg", "searchAttrs").
                subAggregation(AggregationBuilders.terms("attrIdAgg").field("searchAttrs.attrId").
                        subAggregation(AggregationBuilders.terms("attrNameAgg").field("searchAttrs.attrName")).
                        subAggregation(AggregationBuilders.terms("attrValueAgg").field("searchAttrs.attrValue"))));


        sourceBuilder.fetchSource(new String[]{"skuId", "title", "subTitle", "price", "defaultImage"}, null);
        System.out.println(sourceBuilder);
        return sourceBuilder;
    }
}
